<template>
	<!-- 监听变量 spath 的变化 -->
	<view
		class="map-container"
		:style="{
			width: clientWidth + 'px',
			transform: `translateX(${windowWidth / 2 - clientWidth / 2 + 'px'})`
		}"
		:treeData="treeData"
		:change:treeData="map.loadTree"
	>
		<svg id="map"></svg>
	</view>
</template>

<script>
import mapData from './mapData.js';
export default {
	props: {
		familyData: {
			type: String,
			default: {}
		}
	},
	data() {
		return {
			treeData: '',
			initData: {
				name: '添加成员',
				isNull: 1
			},
			clientWidth: document?.body.clientWidth || uni.getSystemInfoSync().screenWidth,
			clientHeight: document?.body.clientHeight || uni.getSystemInfoSync().screenHeight,
			windowWidth: window?.innerWidth || uni.getSystemInfoSync().screenWidth,
			windowHeight: window?.innerHeight || uni.getSystemInfoSync().screenHeight
		};
	},
	mounted() {
		this.treeData = this.familyData.name ? this.familyData : this.initData;
		// this.treeData = mapData.webMap;
		// console.log('this.treeData', this.treeData);
	},
	watch: {
		familyData(newValue) {
			this.treeData = newValue.name ? newValue : this.initData;
			// console.log('this.treeData11', this.treeData);
		}
	},
	methods: {
		/**
		 * 接收 map 中传回的数据
		 * @param {Object} id
		 */
		reciveMessage: function (data) {
			this.$emit('clickFamily', data);
		}
	}
};
</script>

<script module="map" lang="renderjs">
import * as d3 from 'd3';
const nodeWidth = 100
const nodeHeight = 150
// #ifdef H5
const avaterPic ={
	male:'http://112.124.50.179:8888/imgs/home/tx_male.png',
	female:'http://112.124.50.179:8888/imgs/home/tx_female.png',
	no:'http://112.124.50.179:8888/imgs/home/tx.png'
}
const bgcPic ={
	male:'http://112.124.50.179:8888/imgs/home/clanbg_male.png',
	female:'http://112.124.50.179:8888/imgs/home/clanbg_female.png',
	no:'http://112.124.50.179:8888/imgs/home/tx_bg.png'
}
// #endif

// #ifndef H5
const bgcPic ={
	male:'http://192.168.1.240:28088/file/64bde82b5c395f07f7e98d063d10db340dd48c3fclanbg_male.png',
	female:'http://192.168.1.240:28088/file/0c157b6a8e71a3ae2511d576557e5d1333739e61clanbg_female.png',
	no:'http://192.168.1.240:28088/file/b018ba1d744de004659837446b11844935297f95tx_bg.png'
}
const avaterPic ={
	male:'http://192.168.1.240:28088/file/d47b21a89006760a982dd6f069c5fa7162de629ftx_male.png',
	female:'http://192.168.1.240:28088/file/00e8783ad6c5d55c91ed65393f450f816d073089tx_female.png',
	no:'http://192.168.1.240:28088/file/8e75dd2e3f7e3012d25fd0cebad2513fccf8a9f3tx_male.png'
}
// #endif

export default {
	methods: {
		loadTree(newValue, oldValue, ownerInstance, instance) {
			// 监听 service 层数据变更
			// 初始化 绘图
			this.drawMap(newValue)
		},
		drawMap(list) {
			// console.log('list', list)
			const that = this;
			//为空的时候不做处理
			if (!list) {
				return
			}
			//清空之前的svg
			d3.select('#map')
				.selectAll('svg').remove()
			let svg = d3.select('#map')
				.append('svg')
				.attr('width', '1000')
				.attr('height', '1000')
				.attr('id', 'svgWrapper');

			// #map 为页面 svg 的 id
			const svgContainer = d3.select('#map').node();

			// 设置 放大缩小的倍数，这里设置的是 0.5-8 倍
			const zoom = d3.zoom()
				.scaleExtent([.5, 8])
				.on("zoom", zoomed);

			const g = svg.append("g").attr('id', 'gContainer');
			let g2 = g.append("g")

			// 将源数据转换为可以生成树状图的数据(有节点 nodes 和连线 links )
			let treeData = d3
				.tree()
				// 设置每个节点的尺寸,给每个结点指定固定大小
				.nodeSize(
					// 节点包含后方的连接线 [节点宽度，节点高度]
					[nodeWidth, nodeHeight]
				)
				// 设置树状图节点之间的垂直间隔
				.separation(function(a, b) {
					// 样式一：节点间等间距
					// return (a.parent == b.parent ? 1: 2) / a.depth;
					// 样式二：根据节点子节点的数量，动态调整节点间的间距
					let rate =
						(a.parent == b.parent ? (b.children ? b.children.length / 2 : 1) : 2) /
						a.depth;

					if (a.data.partner || b.data.partner) {
						rate = rate + 0.6
					}
					if (a.data.partner && b.data.partner) {
						rate = rate + 0.6
					}
					// 间距比例不能小于0.7，避免间距太小而重叠
					if (rate < 0.9) {
						rate = 0.9;
					}
					return rate;
				})(
					// 创建层级布局，对源数据进行数据转换
					d3.hierarchy(list).sum(function(node) {
						// 函数执行的次数，为树节点的总数,node为每个节点
						return node.value;
					})
				);
			// console.log('treeData.descendants()', treeData)

			//绘制每层最左侧的主题
			const layers = [...new Set(treeData.descendants().map(d => d.depth))];
			const leftNodePostion = that.getLeftNodePostion(treeData)
			layers.forEach((depth, item) => {
				if(treeData.data.isNull){
					return
				}

				//几代
				let text = g2.append("text")
					.attr("x", leftNodePostion[item] - nodeWidth / 2 - 10)
					.attr("y", (item) * nodeHeight + nodeHeight / 2 - 10)
					.style('font-weight','bold')
					.style("text-anchor", "end")
				let dataArr =["【", `${that.numToChinese(depth+1)}世`,'】辈']
				text.select(function() { return this; })
				    .selectAll("tspan")
				    .data(dataArr)
				    .enter()
				    .append("tspan")
				    .attr("x", function(d, i){
						let leftWidth=0
						dataArr.forEach((data,index)=>{
							if(i>index){
								leftWidth = leftWidth +that.getPxwidth(data)
							}
						})
						if(i == 1){
							leftWidth = leftWidth +5
						}
						if(i == 2){
							leftWidth = leftWidth -15
						}
						return leftNodePostion[item] - that.getPxwidth(`【${that.numToChinese(depth+1)}世】辈`) + leftWidth
					})
				    .attr("y", (item) * nodeHeight + nodeHeight / 2 - 10)
				    .text(function(d) { return d; })
				    .style("fill", function(d, i) { return i==1 ? "#FF4C5A" : "#333333"; }); // 根据条件设置颜色

				let genderNum = that.getGenderNum(treeData)

				//男女人数
				g2.append("text")
					.attr("x", leftNodePostion[item] - nodeWidth / 2 - 22)
					.attr("y", (item) * nodeHeight + nodeHeight / 2 - 10 + 23)
					.style("text-anchor", "end")
					.style('fill','#666666')
					.text(`男${genderNum[item].male}女${genderNum[item].female}`)
			});

			//路径
			let diagonal = (node, s, d) => {
				if (node.source.data.partner && !node.target.data.partner) {
					//上面有伴侣
					return `M ${s.x - 4} ${s.y + 125} ${s.x+4} ${s.y + 125}
							${s.x} ${s.y + 125},
							${s.x} ${d.y - 10},
				            ${d.x} ${d.y - 10},
							${d.x} ${d.y - 10},
							${d.x} ${d.y},
				            `;
				} else if (!node.source.data.partner && node.target.data.partner) {
					//下面有伴侣
					return `M ${s.x} ${s.y + 125} ${s.x} ${d.y - 10}
				            ${d.x - 28} ${d.y - 10},
							${d.x - 28} ${d.y - 10},
							${d.x - 28} ${d.y},
				            `;
				} else if (node.source.data.partner && node.target.data.partner) {
					//都有伴侣
					return `M ${s.x - 4} ${s.y + 125} ${s.x+4} ${s.y + 125}
						   ${s.x} ${s.y + 125},
						   ${s.x} ${d.y - 10},
				           ${d.x - 28} ${d.y - 10},
				           ${d.x - 28} ${d.y},
				            `;
				} else {
					//如果没有伴侣
					return `M ${s.x} ${s.y + 125} ${s.x} ${d.y - 10}
				            ${d.x} ${d.y - 10},
							${d.x} ${d.y - 10},
							${d.x} ${d.y},
				            `;
				}
			};

			//绘制边
			g2
				.selectAll('path')
				// 节点的关系 links
				.data(treeData.links())
				.enter()
				.append('path')
				.attr('d', function(d) {
					// 根据name值的长度调整连线的起点
					var start = {
						x: d.source.x,
						y: d.source.y
					};
					var end = {
						x: d.target.x,
						y: d.target.y
					};
					return diagonal(d, start, end);
					// return Bézier_curve_generator({ source: start, target: end });
				})
				.attr('fill', 'none')
				.attr('stroke', '#84785B')
				.attr('stroke-width', 2);

			//绘制节点
			let groups = g2
				.selectAll('g')
				// 节点 nodes
				.data(treeData.descendants())
				.enter()
				.append('g')
				.attr('transform', function(d) {
					var cx = d.x;
					var cy = d.y;
					return 'translate(' + cx + ',' + cy + ')';
				})


			//背景图片
			groups.append("image")
				.attr("xlink:href", function(d) {
					if (d.data.isNull == 1) {
						return bgcPic.no //棕色
					} else {
						if (d.data.gender == 2) {
							return bgcPic.female //红色
						} else {
							return bgcPic.male //蓝色
						}
					}
				})
				.attr('y', 0)
				.attr('x', function(d) {
					if (d.data.partner) {
						//如果有配偶
						return -60
					} else {
						return -28
					}
				})
				.attr('width', 56)
				.attr('height', 127)
				.on("click", clicked);

			groups.append("image")
				.attr("xlink:href", function(d) {
					if (d.data.partner?.gender == 2) {
						return bgcPic.female //红色
					} else {
						return bgcPic.male //蓝色
					}
				})
				.attr('y', 0)
				.attr('x', 4)
				.attr('width', function(d) {
					return d.data.partner ? 56 : 0;
				})
				.attr('height', 127)
				.on("click", clickPartner);

			//头像图片
			groups.append("circle")
				.attr('cy', 30)
				.attr('cx', function(d) {
					if (d.data.partner) {
						//如果有配偶
						return -32
					} else {
						return 0
					}
				})
				.attr('r', 20)
				.style('fill', function(node) {
					return "url(#" + "pattern" + node.data.kinsfolkId + ")";
				})


			groups.append("circle")
				.attr('cy', 30)
				.attr('cx', 32)
				.attr('r', function(d) {
					if (d.data.partner) {
						//如果有配偶
						return 20
					} else {
						return 0
					}
				})
				.style('fill', function(node) {
					if (!node.data.partner) {
						return
					}
					return "url(#" + "pattern" + node.data.partner.kinsfolkId + ")";
				})


			//剪裁头像图片为圆形
			groups.append("pattern")
				.attr("id", function(node) {
					return "pattern" + node.data.kinsfolkId;
				})
				.attr("x", 0)
				.attr("y", 0)
				.attr('width', 40)
				.attr('height', 40)
				.append("svg:image")
				.attr("xlink:href", function(d) {
					if (!d.data.headUrl) {
						//没有设置头像
						if (d.data.isNull == 1) {
							return avaterPic.no //棕色
						} else {
							if (d.data.gender == 2) {
								return avaterPic.female //红色
							} else {
								return avaterPic.male //蓝色
							}
						}
					} else {
						return d.data.headUrl
					}
				})
				.attr("x", 0)
				.attr("y", 0)
				.attr('width', 40)
				.attr('height', 40)
				.attr("preserveAspectRatio", "none")

			groups.append("pattern")
				.attr('width', function(d) {
					if (d.data.partner) {
						//如果有配偶
						return 40
					} else {
						return 0
					}
				})
				.attr("id", function(node) {
					return "pattern" + node.data.partner?.kinsfolkId;
				})
				.attr("x", 0)
				.attr("y", 0)
				.attr('height', 40)
				.append("svg:image")
				.attr("xlink:href", function(d) {
					if (!d.data.partner) {
						return
					}

					if (!d.data.partner.headUrl) {
						//没有设置头像
						if (d.data.partner.gender == 2) {
							return avaterPic.female //红色
						} else {
							return avaterPic.male //蓝色
						}
					} else {
						return d.data.partner.headUrl
					}
				})
				.attr("x", 0)
				.attr("y", 0)
				.attr('width', 40)
				.attr('height', 40)
				.attr("preserveAspectRatio", "none")

			//绘制文字
			groups
				.append('text')
				.attr('fill', function(d) {
					if (d.data.isNull == 1) {
						return '#84785B' //棕色
					} else {
						if (d.data.gender == 2) {
							return "#E57BA9" //红色
						} else {
							return "#13ACFC" //蓝色
						}
					}
				})
				.style('writing-mode', 'tb-rl')
				.attr('textLength', function(d) {
					if (d.data.name.length > 3) {
						return 60
					} else {
						return 54
					}
				})
				.attr('font-weight', function(d) {
					return 'bold';
				})
				.attr('font-size', 14)
				.attr('cursor', function(d) {
					return 'pointer';
				})
				.attr('y', 56)
				.attr('x', function(d) {
					if (d.data.partner) {
						//如果有配偶
						return -32
					} else {
						return 0
					}
				})
				.attr('slot', function(d) {
					return d.data.prop;
				})
				.text(function(d) {
					return d.data.name;
				});

			groups
				.append('text')
				.attr('fill', function(d) {
					if (!d.data.partner) {
						return '#fff'
					}
					if (d.data.partner?.gender == 2) {
						return "#E57BA9" //红色
					} else {
						return "#13ACFC" //蓝色
					}
				})
				.style('writing-mode', 'tb-rl')
				.attr('textLength', function(d) {
					if (d.data.name.length > 3) {
						return 60
					} else {
						return 54
					}
				})
				.attr('font-weight', function(d) {
					return 'bold';
				})
				.attr('font-size', 14)
				.attr('cursor', function(d) {
					return 'pointer';
				})
				.attr('y', 56)
				.attr('x', 32)
				.attr('slot', function(d) {
					return d.data.partner?.prop;
				})
				.text(function(d) {
					return d.data.partner?.name;
				});


			// 下面这段代码是为了能够让绘制出来的图自适应屏幕大小，显示全部图形，如不需要可注释
			// ============== 自适应 开始 ====================
			let svgWrapper = d3.select('#gContainer').node().getBoundingClientRect() || d3.select('#gContainer').node()
				.getClientRects()[0];
			// 获取 svg 的 宽和高
			let svgWrapperWidth = Math.floor(svgWrapper.width);
			let svgWrapperHeight = Math.floor(svgWrapper.height);
			// 这里的 350 和 400 可以自行定义，暂未找到适用所有 svg 图形的比例，如有更好的方法可以提出来参考一下
			let scale
			if (svgWrapperWidth > document.body.clientWidth - 20) {
				scale = (document.body.clientWidth - 20) / svgWrapperWidth;
			} else {
				scale = 1
			}
			const newWidth = svgWrapperWidth * scale;
			const newHeight = svgWrapperHeight * scale;
			const x = svgWrapper.left * scale;
			const y = svgWrapper.top * scale;

			// 获取屏幕宽度， 屏幕高度
			// #ifdef H5
			const clientWidth = window.innerWidth;
			const clientHeight = window.innerHeight
			g2.attr("transform",
				// 使左右、上下空白区域平分
				`translate(${-(x-(clientWidth-newWidth)/2)} ${(clientHeight-newHeight)/2-y}) scale(${scale})`)
			// #endif

			// #ifndef H5
			const clientWidth = document.body.clientWidth;
			const clientHeight = document.body.clientHeight;
			g2.attr("transform",
				// 使左右、上下空白区域平分
				`translate(${-(x-(clientWidth-newWidth)/2)} ${(clientHeight-newHeight)/2-y}) scale(${scale})`)
			// #endif
			// ============== 自适应 结束 ====================

			svg.call(zoom);

			// 点击操作
			function clicked(event, d) {
				event.stopPropagation();
				that.sendMsg(d.data)
			}

			/**
			 * 点击伴侣
			 * @param {Object} event
			 * @param {Object} d
			 */
			function clickPartner(event, d) {
				event.stopPropagation();
				that.sendMsg(d.data.partner)
			}

			function zoomed(event) {
				const {
					transform
				} = event;
				g.attr("transform", transform);
				g.attr("stroke-width", 1 / transform.k);
			}
		},
		sendMsg(data) {
			// 向父组件传参
			this.$ownerInstance.callMethod('reciveMessage', data)
		},
		/**
		 * 获取每一层男女人数
		 * @param {Object} treeData
		 */
		getGenderNum(treeData){
			// 创建一个对象来存储每一层的节点数量
			const genderNum = {};

			// 遍历所有节点，并计算每一层的节点数量
			treeData.descendants().forEach(node => {
				if(!genderNum[node.depth]){
					genderNum[node.depth]={
						female:0,
						male:0
					}
				}
				if(node.data.gender == 1){
					genderNum[node.depth].male++
				}else{
					genderNum[node.depth].female++
				}

				if(node.data.partner){
					if(node.data.partner.gender == 1){
						genderNum[node.depth].male++
					}else{
						genderNum[node.depth].female++
					}
				}
			});

			return genderNum
		},
		/**
		 * 获取每一层有多少个节点
		 * @param {Object} layerNum
		 */
		getLayerNodeNum(treeData) {
			// 创建一个对象来存储每一层的节点数量
			const nodeCountsPerLevel = {};

			// 遍历所有节点，并计算每一层的节点数量
			treeData.descendants().forEach(node => {
				if(!nodeCountsPerLevel[node.depth]){
					nodeCountsPerLevel[node.depth]=0
				}
				if (node.data.partner) {
					nodeCountsPerLevel[node.depth]++;
				}
				nodeCountsPerLevel[node.depth]++;
			});

			return nodeCountsPerLevel
		},
		/**
		 * 获取每层最左侧节点的位置
		 * @param {Object} treeData
		 */
		getLeftNodePostion(treeData) {
			let nodeLeftPostions = {}
			treeData.descendants().forEach(node => {
				let x = node.x
				if (node.data.partner) {
					x = x - nodeWidth / 2 + 15
				}
				if (!nodeLeftPostions[node.depth] || nodeLeftPostions[node.depth] > x) {
					nodeLeftPostions[node.depth] = x
				}
			});
			return nodeLeftPostions
		},
		//计算字符串的像素长度
		getPxwidth(str, fontSize = '25px', fontFamily = 'Microsoft YaHei'){
			var span = document.createElement('span');
			var result = {};
			result.width = span.offsetWidth;
			result.height = span.offsetHeight;
			span.style.visibility = 'hidden';
			span.style.fontSize = fontSize;
			span.style.fontFamily = fontFamily;
			span.style.display = 'inline-block';
			document.body.appendChild(span);
			if (typeof span.textContent != 'undefined') {
				span.textContent = str;
			} else {
				span.innerText = str;
			}
			result.width = parseFloat(window.getComputedStyle(span).width) - result.width;
			// 字符串的显示高度
			// result.height = parseFloat(window.getComputedStyle(span).height) - result.height;
			return result.width;
		},
		//数字换成中文
		numToChinese(num) {
		    const numChars = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
		    const posChars = ['', '十', '百', '千'];
		    const sectionChars = ['', '万', '亿', '万亿'];

		    if (num === 0) {
		        return '零';
		    }

		    let str = '';
		    let sectionIndex = 0;
		    while (num > 0) {
		        let section = num % 10000;
		        if (section !== 0) {
		            let sectionStr = '';
		            let unit = 0;
		            while (section > 0) {
		                let digit = section % 10;
		                if (digit !== 0) {
		                    sectionStr = numChars[digit] + posChars[unit] + sectionStr;
		                } else if (!sectionStr.startsWith('零')) {
		                    sectionStr = '零' + sectionStr;
		                }
		                section = Math.floor(section / 10);
		                unit++;
		            }
		            str = sectionStr + sectionChars[sectionIndex] + str;
		        }
		        sectionIndex++;
		        num = Math.floor(num / 10000);
		    }
		    return str;
		}
	},
}

// 获取字符串的像素宽度
function getPXwidth(str, fontSize = '12px', fontFamily = 'Microsoft YaHei') {
	var span = document.createElement('span');
	var result = {};
	result.width = span.offsetWidth;
	result.height = span.offsetHeight;
	span.style.visibility = 'hidden';
	span.style.fontSize = fontSize;
	span.style.fontFamily = fontFamily;
	span.style.display = 'inline-block';
	document.body.appendChild(span);
	if (typeof span.textContent != 'undefined') {
		span.textContent = str;
	} else {
		span.innerText = str;
	}
	result.width = parseFloat(window.getComputedStyle(span).width) - result.width;
	// 字符串的显示高度
	// result.height = parseFloat(window.getComputedStyle(span).height) - result.height;
	return result.width;
}
</script>

<style>
.map-container {
	width: 100%;
	height: 100%;
}

svg {
	width: 100%;
	height: 100%;
	touch-action: none;
}
</style>
